package com.tydic.workflow.service.impl;

import cn.hutool.core.util.StrUtil;
import com.tydic.workflow.service.IBpmService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.HistoryService;
import org.flowable.engine.IdentityService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.flowable.engine.history.HistoricActivityInstance;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Map;
import java.util.Set;

@Service
@RequiredArgsConstructor
@Slf4j
public class BpmServiceImpl implements IBpmService {
    private final RuntimeService runtimeService;
    private final HistoryService historyService;
    private final TaskService taskService;
    private final IdentityService identityService;

    @Transactional(rollbackFor = Exception.class)
    @Override
    public ProcessInstance startProcessInstanceByKey(String processDefinitionKey, Map<String, Object> variables) {
        return this.startProcessInstanceByKey(processDefinitionKey, null, variables);
    }


    @Transactional(rollbackFor = Exception.class)
    @Override
    public ProcessInstance startProcessInstanceByKey(String processDefinitionKey, String businessKey, Map<String, Object> variables) {
        return runtimeService.startProcessInstanceByKey(processDefinitionKey, businessKey, variables);
    }


    @Override
    public Task getTask(String processInstanceId, String userId) {
        if (StrUtil.isBlank(processInstanceId) || StrUtil.isBlank(userId)) {
            return null;
        }
        return taskService.createTaskQuery().processInstanceId(processInstanceId).taskCandidateOrAssigned(userId).singleResult();
    }

    @Override
    public Task getTask(String processInstanceId) {
        return taskService.createTaskQuery().processInstanceId(processInstanceId).singleResult();
    }


    @Override
    public List<Task> getTasks(String userId, int offset, int limit) {
        return taskService.createTaskQuery().taskCandidateOrAssigned(userId).orderByTaskCreateTime().desc().listPage(offset, limit);
    }


    @Transactional(rollbackFor = Exception.class)
    @Override
    public void unclaim(String taskId) {
        taskService.unclaim(taskId);
    }


    @Transactional(rollbackFor = Exception.class)
    @Override
    public void claim(String taskId, String userId) {
        taskService.claim(taskId, userId);
    }


    @Transactional(rollbackFor = Exception.class)
    @Override
    public void complete(String taskId) {
        this.complete(taskId, null);
    }


    @Transactional(rollbackFor = Exception.class)
    @Override
    public void complete(String taskId, Map<String, Object> variables) {
        this.complete(taskId, variables, null);
    }


    @Transactional(rollbackFor = Exception.class)
    @Override
    public void complete(String taskId, Map<String, Object> variables, String comment) {
        if (StrUtil.isNotBlank(comment)) {
            Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
            taskService.addComment(taskId, task.getProcessInstanceId(), comment);
        }
        taskService.complete(taskId, variables);
    }


    @Transactional(rollbackFor = Exception.class)
    @Override
    public void delegate(String taskId, String userId) {
        taskService.delegateTask(taskId, userId);
    }


    @Transactional(rollbackFor = Exception.class)
    @Override
    public void setAssignee(String taskId, String userId) {
        taskService.setAssignee(taskId, userId);
    }


    @Transactional(rollbackFor = Exception.class)
    @Override
    public void resolveTask(String taskId) {
        taskService.resolveTask(taskId);
    }


    @Transactional(rollbackFor = Exception.class)
    @Override
    public void withdraw(String processInstanceId, String currentActivityId, String newActivityId) {
        runtimeService.createChangeActivityStateBuilder()
            .processInstanceId(processInstanceId)
            .moveActivityIdTo(currentActivityId, newActivityId)
            .changeState();
    }


    @Override
    public List<HistoricTaskInstance> getHistoryTasks(String processInstanceId) {
        return historyService.createHistoricTaskInstanceQuery().processInstanceId(processInstanceId).orderByHistoricTaskInstanceStartTime().asc().list();
    }


    @Override
    public List<HistoricActivityInstance> getHistoryActivities(String processInstanceId) {
        return historyService.createHistoricActivityInstanceQuery().processInstanceId(processInstanceId).orderByHistoricActivityInstanceStartTime().asc().list();
    }

    @Override
    public List<HistoricProcessInstance> getHistoryProcessInstances(Set<String> processInstanceIds) {
        return historyService.createHistoricProcessInstanceQuery().processInstanceIds(processInstanceIds).orderByProcessInstanceStartTime().asc().list();
    }

    @Override
    public HistoricProcessInstance getHistoryProcessInstance(String processInstanceId) {
        return historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void setAuthenticatedUserId(String userId) {
        identityService.setAuthenticatedUserId(userId);
    }

}
